By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 Style guide.md

View raw Download
text/x-script.python • 11.33 kiB
Python script, ASCII text executable

Style Guide for Python Code in the roundabout project

This document provides conventions for the coding style used in the Roundabout project. Rules can be broken -- just try to make the code as readable as you can.

Code should be written for Python 3.10 unless I say otherwise.

Code Layout

Indents and Line Continuation

Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace.

For parentheses, three styles are accepted:

# No indent.
thing = function(arg1, arg2)

# Hanging indent. Align to the contents, not to the bracket.
thing = function(arg1, arg2
                arg3, arg4, arg5)

# Full indent. Here only one argument or variable or whatever is allowed per line.
# The closing bracket must be at the previous indent level.
# Any level is allowed, but it must be a multiple of 4.
thing = function(
   arg1,
   arg2,
   arg3,
   arg4
)

Collections should read like lists, not tables.

fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",]

fruits = [
   "apple",
   "tomato",
   "pear",
   "cherry",
   "plum",
   "melon",
   "grape",
   "aubergine",
]

# Don't!
fruits = [
   "apple", "tomato", "pear", "cherry",
   "plum", "melon", "grape", "aubergine",
]

Additionally, for a collection meant to be expanded use a trailing comma.

For long expressions, begin the line with the operator and align the operand with the indentation level. This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts.

population = (population
           + births
           + immigrants
           - deaths
           - emigrants)

When the hanging indent could be mistaken for a block, add an empty line.

Backslashes are discouraged, but allowed if it is the only way to write your expression. For example:

really_long_variable_name_hopefully_yours_wont_be_as_long = \
   "Really long value."

Blank Line Rules

  • Two between top-level class or function definitions.

  • One between local functions or methods.

  • One is allowed to separate related groups and logical sections.

  • One at the end of the file.

  • One below imports.

Imports

Imports should be on separate lines. from-imports must import all objects on the same line though.

import flask
import os

from models import User, Post

Imports should be ordered like this:

  • __future__ statements

  • one blank line

  • magic names (__all__, __version__)

  • one blank line

  • library imports

  • library from-imports

  • one blank line

  • application imports

  • application from-imports

Wildcard imports are discouraged and prohibited for libraries.

Interior Whitespace

Never insert more than one space around operators and other symbols. Never add spaces just to align lines.

When to use

  • around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow

    # Do
    thing += 10 + 2*thing
    # Don't
    thing+=10+2 * thing
    # Acceptable; use your best judgement
    thing += 10 + 2 * thing
    
  • after the colon when defining a single-line clause, or an annotation

  • after commas or semicolons

  • after the comment sign #

    When to avoid

  • when passing keyword arguments or defining argument defaults, unless they are annotated

  • inside any brackets

    # Do
    function(arg1, arg2)
    # Don't!
    function( arg1, arg2 )
    
  • after a trailing comma

    # Do
    (0,)
    # Don't!
    (0, )
    
  • before commas, semicolons or colons

    # Do
    x, y = y, x
    # Don't!
    x , y = y , x
    
  • around the slice operator :

  • before an argument list

    # Do
    function(arg1, arg2)
    # Don't!
    function (arg1, arg2)
    
  • before the indexing operator

    # Do
    dictionary["key"] = "Hello World!"
    # Don't!
    dictionary ["key"] = "Hello World!"
    

Other

Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses.

Strings

Only use double quotes like " for strings: single quotes are harder to spot, and they conflict with the common apostrophe.

For docstrings you should follow PEP 257. Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs. HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code. Other than this, they should be kept plaintext to prevent markup language battles (Python prefers RST, but most use Markdown).

Naming

Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be made sure that they are widely used and accepted already. (id for identifier or repo for repository is acceptable, but avg for average is not). Generally, names should be easy to pronounce.

Class names must use UpperCamelCase with the first letter of each word capitalised and no underscores.

Other names (function, variable) should use snake_case with all words lowercase and separated by underscores.

For instance and class methods, only name the first argument self or cls, respectively.

To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (class_ is better than clss or klass or classs or whatever).

Constants are an exception. They should use UPPER_CASE with underscores.

Non-public names should be prefixed with an underscore. In case it's really important to not use them, use a double underscore to invoke name mangling.

Comments

Block comments should be complete sentences; line comments don't need to. Write comments in English and always use a space after the comment sign, as stated above, unless it's an UNIX interpreter descriptor (#!) where you should not. Inside block comments, separate paragraphs with an empty comment, like in Markdown. For a solo sentence, full stops are optional. Avoid stating the obvious or contradicting the code.

Inline comments must be separated with more than one space from the statement, and they may be aligned.

In comments, never alter the case of identifiers, as it may lead to confusion.

Leaving TODO comments as personal notes is allowed, but they should be removed before merging or a release.

Programming

OOP Guidelines

Do not use getters and setters for class attributes. If you do need to change some other things, use properties.

Prefer overloading the operators; make using your objects as natural and Pythonic as possible.

Exceptions

Make exceptions specific enough, so catching them can be explicit.

Do not use the bare except clause. Make try clauses as short as possible to avoid silencing unrelated bugs.

Other

Comparisons to singletons (True, False, None etc.) should be done with the identity operators is and is not, not with the comparison operators. Use is not, not not ... is.

Unless it would be ambiguous, use the implicit truth test to check that numbers are different to 0, that containers have contents and similar tests.

Do not assign lambdas to identifiers. Make a real function instead.

Use with context managers to ensure resources are properly cleaned up.

Prefer making functions that take arguments and return a value instead of making them directly take global variables or process the information such as writing.

Use the methods startswith() and endswith() instead of string slicing to check for prefixes or suffixes.

To compare types, use isinstance() instead of the is operator with type() (this is one of my problems with Python, but it's the standard).

Use a proper condition for while instead of a while True that breaks.

Use for loops instead of while loops when possible, and use Pythonic iteration instead of C-style iteration.

To call shells, use the subprocess module instead of os.system() and use the functions that allow giving a list of arguments instead of a string, it leads to better security.

Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra careful.

Jinja style guide

Jinja should be written like Python, with the following additions:

Tags should be written as {% <content> %} and expressions as {{ <content> }}. That is, put spaces around the content.

Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags and it wouldn't disrupt whitespace, you should indent the contents.

The filter operator | should have spaces around it, unless it's in a more complex expression when it shouldn't.

Translations should always be done with the {% trans %} tag provided by Babel, not with gettext(), _() or others. No exceptions.

The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should use single quotes, as HTML takes precedence.

HTML style guide

HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes.

Always indent tag contents with 4 spaces, except in plaintext tags like pre or textarea, where you should not indent.

The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout.

IDs or classes should be written in kebab-case. Names should be written in snake_case to provide better compatibility with Python.

Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be standardised in the future.

Event attributes are allowed as well, but please keep the JS inside shorter than 64 characters. If you need more make a function in a script tag or a separate file.

CSS style guide

CSS selectors, properties and values should be written in lowercase. Custom properties should be written in kebab-case.

Always indent the contents of a ruleset with 4 spaces.

Unlike some other style guides, we do not require each selector after a comma to be on a new line. However, if they're too long, very complex or similar and they benefit from alignment, you should do so.

IDs are preferred over classes when the element only appears once on the page.

Tag selectors are allowed! Style the default widgets as you see fit, because it leads to cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent. In what scenario would you want an unstyled button in your site? Never! Then why always use <button class="btn btn-primary"> when it's the only kind of <button> your site has?

However, provide class-based alternatives for tag styles. For example, Efficient UI styles button by default, but it also styles .button to allow hyperlinks or other elements to look like buttons. Using both isn't needed though.

Also, selectors can be nested where it makes sense, however the > selector is preferred over plain nesting, which is generally discouraged.

Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got server-side templating, profit from it!

JavaScript style guide

JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces. Otherwise I can't comment, because JS is ugly by nature.

                
                    
1
Style Guide for Python Code in the roundabout project
2
=====================================================
3
4
This document provides conventions for the coding style used in the Roundabout project. Rules can be broken -- just try to make the code as readable as you can.
5
6
Code should be written for Python 3.10 unless I say otherwise.
7
8
Code Layout
9
-----------
10
11
### Indents and Line Continuation
12
Keep lines up to 80 characters long. Use 4 spaces for indentation, per level. Do not use tabs, and do not put trailing whitespace.
13
14
For parentheses, three styles are accepted:
15
~~~python
16
# No indent.
17
thing = function(arg1, arg2)
18
19
# Hanging indent. Align to the contents, not to the bracket.
20
thing = function(arg1, arg2
21
arg3, arg4, arg5)
22
23
# Full indent. Here only one argument or variable or whatever is allowed per line.
24
# The closing bracket must be at the previous indent level.
25
# Any level is allowed, but it must be a multiple of 4.
26
thing = function(
27
arg1,
28
arg2,
29
arg3,
30
arg4
31
)
32
~~~
33
34
Collections should read like lists, not tables.
35
~~~python
36
fruits = ["apple", "tomato", "pear", "cherry", "plum", "melon", "grape", "aubergine",]
37
38
fruits = [
39
"apple",
40
"tomato",
41
"pear",
42
"cherry",
43
"plum",
44
"melon",
45
"grape",
46
"aubergine",
47
]
48
49
# Don't!
50
fruits = [
51
"apple", "tomato", "pear", "cherry",
52
"plum", "melon", "grape", "aubergine",
53
]
54
~~~
55
56
Additionally, for a collection meant to be expanded use a trailing comma.
57
58
For long expressions, begin the line with the operator and align the operand with the indentation level.
59
This is not permitted by PEP 8, but it follows mathematics and leads to nicer layouts.
60
~~~python
61
population = (population
62
+ births
63
+ immigrants
64
- deaths
65
- emigrants)
66
~~~
67
68
When the hanging indent could be mistaken for a block, add an empty line.
69
70
Backslashes are discouraged, but allowed if it is the only way to write your expression. For
71
example:
72
73
~~~python
74
really_long_variable_name_hopefully_yours_wont_be_as_long = \
75
"Really long value."
76
~~~
77
78
### Blank Line Rules
79
* Two between top-level class or function definitions.
80
* One between local functions or methods.
81
* One is allowed to separate related groups and logical sections.
82
* One at the end of the file.
83
* One below imports.
84
85
### Imports
86
87
Imports should be on separate lines. `from`-imports must import all objects on the same line though.
88
~~~python
89
import flask
90
import os
91
92
from models import User, Post
93
~~~
94
95
Imports should be ordered like this:
96
* `__future__` statements
97
* one blank line
98
* magic names (`__all__`, `__version__`)
99
* one blank line
100
* library imports
101
* library `from`-imports
102
* one blank line
103
* application imports
104
* application `from`-imports
105
106
Wildcard imports are discouraged and prohibited for libraries.
107
108
### Interior Whitespace
109
Never insert more than one space around operators and other symbols. Never add spaces just to align lines.
110
111
#### When to use
112
* around the lowest priority operators in an expression, comparisons, assignments, and logical operators, as well as the function annotation arrow
113
~~~python
114
# Do
115
thing += 10 + 2*thing
116
# Don't
117
thing+=10+2 * thing
118
# Acceptable; use your best judgement
119
thing += 10 + 2 * thing
120
~~~
121
* after the colon when defining a single-line clause, or an annotation
122
* after commas or semicolons
123
* after the comment sign `#`
124
#### When to avoid
125
* when passing keyword arguments or defining argument defaults, unless they are annotated
126
* inside any brackets
127
~~~python
128
# Do
129
function(arg1, arg2)
130
# Don't!
131
function( arg1, arg2 )
132
~~~
133
* after a trailing comma
134
~~~python
135
# Do
136
(0,)
137
# Don't!
138
(0, )
139
~~~
140
* before commas, semicolons or colons
141
~~~python
142
# Do
143
x, y = y, x
144
# Don't!
145
x , y = y , x
146
~~~
147
* around the slice operator `:`
148
* before an argument list
149
~~~python
150
# Do
151
function(arg1, arg2)
152
# Don't!
153
function (arg1, arg2)
154
~~~
155
* before the indexing operator
156
~~~python
157
# Do
158
dictionary["key"] = "Hello World!"
159
# Don't!
160
dictionary ["key"] = "Hello World!"
161
~~~
162
163
#### Other
164
Never use the semicolon for multiple statements; while allowed, it is discouraged as it hurts readability. Similarly discouraged are one-line clauses.
165
166
Strings
167
-------
168
169
Only use double quotes like `"` for strings: single quotes are harder to spot, and they conflict with the common apostrophe.
170
171
For docstrings you should follow [PEP 257](https://peps.python.org/pep-0257/). Additionally, docstrings should use Markdown to allow for a future tool to convert them to docs.
172
HOWEVER arguments should be described using the ReST syntax, as it is more readable in the code.
173
Other than this, they should be kept plaintext to prevent markup language battles (Python prefers
174
RST, but most use Markdown).
175
176
Naming
177
------
178
179
Identifiers must only use ASCII characters. Abbreviations should be kept to a minimum and it should be
180
made sure that they are widely used and accepted already.
181
(`id` for `identifier` or `repo` for `repository` is acceptable, but `avg` for `average` is not). Generally, names should be easy to pronounce.
182
183
Class names must use `UpperCamelCase` with the first letter of each word capitalised and no underscores.
184
185
Other names (function, variable) should use `snake_case` with all words lowercase and separated by underscores.
186
187
For instance and class methods, only name the first argument `self` or `cls`, respectively.
188
189
To avoid conflicts, append an underscore or use a synonym but do not corrupt the spelling (`class_` is better than `clss` or `klass` or `classs` or whatever).
190
191
Constants are an exception. They should use `UPPER_CASE` with underscores.
192
193
Non-public names should be prefixed with an underscore. In case it's really important to not use
194
them, use a double underscore to invoke name mangling.
195
196
Comments
197
--------
198
199
Block comments should be complete sentences; line comments don't need to. Write comments in
200
English and always use a space after the comment sign, as stated above, unless it's an UNIX
201
interpreter descriptor (`#!`) where you should not. Inside block comments, separate paragraphs
202
with an empty comment, like in Markdown. For a solo sentence, full stops are optional.
203
Avoid stating the obvious or contradicting the code.
204
205
Inline comments must be separated with more than one space from the statement, and they may be
206
aligned.
207
208
In comments, never alter the case of identifiers, as it may lead to confusion.
209
210
Leaving TODO comments as personal notes is allowed, but they should be removed before merging
211
or a release.
212
213
Programming
214
-----------
215
216
### OOP Guidelines
217
Do not use getters and setters for class attributes. If you do need to change some other things,
218
use properties.
219
220
Prefer overloading the operators; make using your objects as natural and Pythonic as possible.
221
222
### Exceptions
223
Make exceptions specific enough, so catching them can be explicit.
224
225
Do not use the bare `except` clause. Make `try` clauses as short as possible to avoid silencing
226
unrelated bugs.
227
228
### Other
229
Comparisons to singletons (`True`, `False`, `None` etc.) should be done with the identity operators
230
`is` and `is not`, not with the comparison operators. Use `is not`, not `not ... is`.
231
232
Unless it would be ambiguous, use the implicit truth test to check that numbers are different to
233
0, that containers have contents and similar tests.
234
235
Do not assign lambdas to identifiers. Make a real function instead.
236
237
Use `with` context managers to ensure resources are properly cleaned up.
238
239
Prefer making functions that take arguments and return a value instead of making them directly take
240
global variables or process the information such as writing.
241
242
Use the methods `startswith()` and `endswith()` instead of string slicing to check for prefixes
243
or suffixes.
244
245
To compare types, use `isinstance()` instead of the `is` operator with `type()` (this is one of
246
my problems with Python, but it's the standard).
247
248
Use a proper condition for `while` instead of a `while True` that `break`s.
249
250
Use `for` loops instead of `while` loops when possible, and use Pythonic iteration instead of
251
C-style iteration.
252
253
To call shells, use the `subprocess` module instead of `os.system()` and use the functions
254
that allow giving a *list* of arguments instead of a string, it leads to better security.
255
256
Similarly, avoid writing SQL manually, use Alchemy. If you must write SQL manually, be extra
257
careful.
258
259
Jinja style guide
260
-----------------
261
262
Jinja should be written like Python, with the following additions:
263
264
Tags should be written as `{% <content> %}` and expressions as `{{ <content> }}`. That is,
265
put spaces around the content.
266
267
Always indent tag contents, just like you would indent HTML tags! If a tag contains other tags
268
and it wouldn't disrupt whitespace, you should indent the contents.
269
270
The filter operator `|` should have spaces around it, unless it's in a more complex expression
271
when it shouldn't.
272
273
Translations should always be done with the `{% trans %}` tag provided by Babel, not with
274
`gettext()`, `_()` or others. No exceptions.
275
276
The quoting rules are as in Python, unless it's in an HTML attribute, in which case you should
277
use single quotes, as HTML takes precedence.
278
279
HTML style guide
280
----------------
281
282
HTML tags should be written in lowercase, with attributes in lowercase as well. Attribute values
283
should be quoted with double quotes. Attribute minimisation is suggested for boolean attributes.
284
285
Always indent tag contents with 4 spaces, except in plaintext tags like `pre` or `textarea`, where
286
you should not indent.
287
288
The tag should be multiple lines if the content is complex. Otherwise, mirror the page layout.
289
290
IDs or classes should be written in `kebab-case`. Names should be written in `snake_case` to
291
provide better compatibility with Python.
292
293
Also, when making custom tags, always use a hyphen in the tag name, to make sure they won't be
294
standardised in the future.
295
296
Event attributes are allowed as well, but please keep the JS inside shorter than 64 characters.
297
If you need more make a function in a script tag or a separate file.
298
299
CSS style guide
300
---------------
301
302
CSS selectors, properties and values should be written in lowercase. Custom properties should be
303
written in `kebab-case`.
304
305
Always indent the contents of a ruleset with 4 spaces.
306
307
Unlike some other style guides, we do not require each selector after a comma to be on a new line.
308
However, if they're too long, very complex or similar and they benefit from alignment, you should
309
do so.
310
311
IDs are preferred over classes when the element only appears once on the page.
312
313
Tag selectors are **allowed**! Style the default widgets as you see fit, because it leads to
314
cleaner HTML. We also apply a reset stylesheet to make sure the default styles are consistent.
315
In what scenario would you want an *unstyled* button in your site? Never! Then why always use
316
`<button class="btn btn-primary">` when it's the only kind of `<button>` your site has?
317
318
However, provide class-based alternatives for tag styles. For example, Efficient UI styles
319
`button` by default, but it also styles `.button` to allow hyperlinks or other elements to look
320
like buttons. Using both isn't needed though.
321
322
Also, selectors can be nested where it makes sense, however the `>` selector is preferred over
323
plain nesting, which is generally discouraged.
324
325
Use of fancy counters and data-attributes is allowed, but only for cosmetic purposes. We've got
326
server-side templating, profit from it!
327
328
JavaScript style guide
329
----------------------
330
331
JS should use double quotes for strings, and lowerCamelCase for names, and indenting should be 4 spaces.
332
Otherwise I can't comment, because JS is ugly by nature.
333